#
# Copyright 2009 Red Hat, Inc. and/or its affiliates.
#
# Licensed to you under the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the files README and
# LICENSE_GPL_v2 which accompany this distribution.
#


import os
import stat
import glob
import logging

import constants
import misc
import multipath
import storage_exception as se
import lvm

RHAT_PV_TAG = "RHAT_PV"
METADATASIZE = 100 * 1024 * 1024

def getPvList():
    # Fetch PV names
    cmd = [constants.EXT_PVS, "--noheadings", "-o", "pv_name", "--nosuffix"]
    cmd = lvm.addExtraCfg(cmd)
    (rc, out, err) = misc.execCmd(cmd)
    if rc != 0:
        raise se.CouldNotRetrievePhysicalVolumeList("error code: %s" % rc)
    return out


log = logging.getLogger('irs')
class PV:
    """
    PV (Physical Volume) interface
    """
    def __init__(self, name, pvdata=None, lvmFilter=None):
        self.guid = os.path.basename(name)
        self.devname = os.path.join("/dev/mapper", self.guid)

        # Validate the device is indeed a block device
        try:
            if not stat.S_ISBLK(os.stat(self.devname).st_mode):
                raise se.InvalidPhysDev()
        except OSError:
            raise se.InvalidPhysDev()

        if multipath.isPartitioned(self.devname):
            raise se.PartitionedPhysDev()

        self.uuid = ""
        self.capacity = 0
        self.vguuid = ""
        self.pecount = self.pealloc = 0
        self.initialized = False
        self.vendor  = None
        self.product = None
        self.pathlist = None
        self.connections = None

        self.refresh(pvdata, lvmFilter)


    @classmethod
    def getAllPVs(cls, fromDevices=None):
        res = {}
        cmd = [constants.EXT_PVS, "--noheadings", "-o",
            "pv_name,pv_uuid,dev_size,vg_uuid,pv_pe_count,pv_pe_alloc_count",
            "--units", "b", "--nosuffix"]

        if fromDevices is not None:
            cmd = lvm.addExtraCfg(cmd, fromDevices)
        else:
            cmd = lvm.addExtraCfg(cmd, "a*")


        (rc, out, err) = misc.execCmd(cmd)
        if rc == 0:
            for line in out:
                try:
                    s = line.split()
                    if len(s) == 5:
                        # This PV does not belong to any VG
                        s.insert(3, "")

                    name = os.path.split(s[0])[1]
                    pvdata = { "uuid" : s[1], "size" : s[2], "vg_uuid" : s[3],
                               "pe_count" : s[4], "pe_alloc_count" : s[5] }
                    tmpPV = PV(name, pvdata)
                    res[name] = tmpPV
                except:
                    log.warn("Could not process PV line `%s`", line, exc_info=True)

        return res


    def refresh(self, pvdata=None, lvmFilter=None):
        """
        Refreshes internal bookkeeping info
        """
        if not pvdata:
            return self._cleanRefresh(lvmFilter)
        return self._updateInfo(pvdata)

    def _updateInfo(self, pvdata):
            self.uuid = pvdata['uuid']
            self.capacity = pvdata['size']
            self.vguuid = pvdata['vg_uuid']
            self.pecount = pvdata['pe_count']
            self.pealloc = pvdata['pe_alloc_count']
            self.initialized = True

    def _cleanRefresh(self, lvmFilter=None):
        # Fetch PV UUID, capacity and VG UUID
        cmd = [constants.EXT_PVS, "--noheadings", "-o",
            "pv_uuid,dev_size,vg_uuid,pv_pe_count,pv_pe_alloc_count",
            "--units", "b", "--nosuffix", self.devname]
        cmd = lvm.addExtraCfg(cmd, devList = lvmFilter)
        (rc, out, err) = misc.execCmd(cmd)
        if rc == 0:
            s = out[0].split()
            if len(s) == 4:
                # This PV does not belong to any VG
                s.insert(2, "")
            (self.uuid, self.capacity, self.vguuid, self.pecount,
                self.pealloc) = s
            self.initialized = True
        else:
            # If this device is not a valid PV, try to get the operating system
            # idea of disk capacity
            cmd = [constants.EXT_BLOCKDEV, "--getsize64", self.devname]
            (rc, out, err) = misc.execCmd(cmd)
            if rc == 0:
                self.capacity = out[0]

    def __str__(self):
        s = self.devname
        if self.initialized:
            s = s + " " + self.uuid + " " + self.capacity
        return s

    def isInitialized(self):
        return self.initialized

    def initialize(self):
        if not self.initialized:
            # Initialize device
            cmd = [constants.EXT_PVCREATE, "--metadatacopies", "0", self.devname]
            cmd = lvm.addExtraCfg(cmd, [self.devname])
            rc = misc.execCmd(cmd)[0]
            if rc == 0:
                self.refresh()
            else:
                raise se.PhysDevInitializationError(self.devname)

    def getInfo(self):
        """
        Returns dictionary of various device characteristics required by
        management application
        """

        if not self.vendor:
            (self.vendor, self.product, self.serial, self.devtype,
                self.connections, self.pathlist) = multipath.pathinfo(self.guid)

        # We return connection list under the label "pathlist" for the sake
        # of backward compatibility.
        info = dict(GUID=self.guid, pvUUID=self.uuid, vgUUID=self.vguuid,
            vendorID=self.vendor, productID=self.product, fwrev="0000",
            serial=self.serial, capacity=self.capacity, devtype=self.devtype,
            pathstatus=self.pathlist, pathlist=self.connections)
        return info


    def reinitMetadata(self):
        # Size for pvcreate should be with units K|M|G
        metadatasize = str(METADATASIZE/1024) + 'K'

        if self.initialized:
            cmd = [constants.EXT_PVCREATE, "--force",
                "--metadatasize", metadatasize, self.devname]
        else:
            cmd = [constants.EXT_PVCREATE, "--metadatasize", metadatasize, self.devname]
        cmd = lvm.addExtraCfg(cmd, [self.devname])
        rc = misc.execCmd(cmd)[0]
        if rc == 0:
            self.refresh()
        else:
            raise se.PhysDevInitializationError(self.devname)

    def validate(self):
        """
        Make sure PV is valid and readable
        """
        cmd = [constants.EXT_PVS, self.devname]
        cmd = lvm.addExtraCfg(cmd, [self.devname])
        (rc, out, err) = misc.execCmd(cmd)

        if rc != 0:
            return False

        return True
